﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Microsoft.Goldenlight.Resources;

namespace Microsoft.Goldenlight.Framework
{
    /// <summary>
    /// 	A Container holds and owns a collection of GameObjects.
    /// </summary>
    /// <remarks>
    /// 	A GameObject can be a member of at most one Container (and must be a member of exactly one once it is registered). When a Container is unregistered, all it's contained objects are also unregistered.
    /// </remarks>
    public class Container : RegisterableObject
    {
        #region Fields & Properties

        /// <summary>
        /// 	True if this is the root container.
        /// </summary>
        public bool IsRoot
        {
            get
            {
                return _parentContainer == null;
            }
        }

        #endregion

        #region Collection Fields & Properties

        private readonly Dictionary<string, Container> _containers = new Dictionary<string, Container>();

        /// <summary>
        /// 	Sub-containers in the current container.
        /// </summary>
        public IEnumerable<Container> Containers
        {
            get
            {
                return _containers.Values;
            }
        }

        /// <summary>
        /// 	Number of sub-containers in the current container.
        /// </summary>
        public int ContainersCount
        {
            get
            {
                return _containers.Values.Count;
            }
        }

        private readonly Dictionary<string, GameObject> _objects = new Dictionary<string, GameObject>();

        /// <summary>
        /// 	Sub-objects in the current container.
        /// </summary>
        public IEnumerable<GameObject> Objects
        {
            get
            {
                return _objects.Values;
            }
        }

        /// <summary>
        /// 	Number of sub-objects in the current container.
        /// </summary>
        public int ObjectsCount
        {
            get
            {
                return _objects.Values.Count;
            }
        }

        #endregion

        #region Overrides of LifetimeObject

        #region Implementation of ICloneable

        /// <summary>
        /// 	Called by Clone. Objects need to implement this method to copy all public properties not marked with TorqueCloneIgnore attribute.
        /// </summary>
        /// <param name="newLifetimeObject"> The object to be copied over. </param>
        public override void CopyTo(LifetimeObject newLifetimeObject)
        {
            base.CopyTo(newLifetimeObject);
            var newContainer = newLifetimeObject as Container;
            if (newContainer != null)
            {
                foreach (var gameObject in _objects.Values.Select(gameObject => gameObject.Clone()).OfType<GameObject>())
                {
                    newContainer.AddObject(gameObject);
                }
                foreach (var container in _containers.Values.Select(container => container.Clone()).OfType<Container>())
                {
                    newContainer.AddContainer(container);
                }
            }
        }

        #endregion

        #region Implementation of IDisposable

        /// <summary>
        /// 	Releases all resources used by the Game class.
        /// </summary>
        /// <param name="disposing"> true to release both managed and unmanaged resources; false to release only unmanaged resources. </param>
        protected override void Dispose(bool disposing)
        {
            _isDisposed = true;
            if (disposing)
            {
                ClearObjects(true);
                ClearContainers(true);
                if (_parentContainer != null)
                {
                    _parentContainer.RemoveContainer(this, false);
                }
            }
            base.Dispose(disposing);
        }

        #endregion

        #region Register & Unregister

        /// <summary>
        /// 	Inserts processing on a lifetime object after it transitions into the registering state which must complete within a specified interval of time.
        /// </summary>
        protected override void OnRegister()
        {
            if (Manager != null)
            {
                foreach (var gameObject in _objects.Values.Where(gameObject => !gameObject.IsRegistered))
                {
                    Manager.Register(gameObject);
                }
                foreach (var container in _containers.Values.Where(container => !container.IsRegistered))
                {
                    Manager.Register(container);
                }
            }
            base.OnRegister();
        }

        /// <summary>
        /// 	Inserts processing on a lifetime object after it transitions to the unregistering state due to the invocation of a synchronous unregister operation.
        /// </summary>
        protected override void OnUnregister()
        {
            if (Manager != null)
            {
                foreach (var gameObject in _objects.Values.Where(gameObject => gameObject.IsRegistered))
                {
                    Manager.Unregister(gameObject);
                }
                foreach (var container in _containers.Values.Where(container => container.IsRegistered))
                {
                    Manager.Unregister(container);
                }
            }
            base.OnUnregister();
        }

        #endregion

        #endregion

        #region Objects Operations

        #region Add & Remove & Clear

        /// <summary>
        /// 	Add a game object to current container. Every object which is added belongs in exactly one container.
        /// </summary>
        /// <param name="gameObject"> New object will be added. </param>
        protected virtual void OnAddObject(GameObject gameObject)
        {
        }

        /// <summary>
        /// 	Remove a game object from current container.
        /// </summary>
        /// <param name="gameObject"> Object will be removed. </param>
        protected virtual void OnRemoveObject(GameObject gameObject)
        {
        }

        /// <summary>
        /// 	Add a game object to current container. Every object which is added belongs in exactly one container.
        /// </summary>
        /// <param name="gameObject"> New object will be added. </param>
        /// <returns> True if added successfully. </returns>
        public bool AddObject(GameObject gameObject)
        {
            if (gameObject == null)
            {
                throw new ArgumentNullException("gameObject");
            }
            if (string.IsNullOrEmpty(gameObject.Name) || gameObject.IsDisposed || gameObject.ParentContainer != null)
            {
                return false;
            }
            if (!_objects.ContainsKey(gameObject.Name))
            {
                if (Manager != null && (gameObject.Manager == null || gameObject.Manager != Manager))
                {
                    return false;
                }
                gameObject.NameChanging += HandleObjectNameChanging;
                gameObject.AddToParentContainer(this);
                _objects.Add(gameObject.Name, gameObject);
                OnAddObject(gameObject);
                return true;
            }
            return false;
        }

        /// <summary>
        /// 	Remove a game object from current container.
        /// </summary>
        /// <param name="gameObject"> Object will be removed. </param>
        /// <returns> True if removed successfully. </returns>
        public bool RemoveObject(GameObject gameObject)
        {
            return RemoveObject(gameObject, false);
        }

        /// <summary>
        /// 	Remove a game object from current container.
        /// </summary>
        /// <param name="gameObject"> Object will be removed. </param>
        /// <param name="dispose"> Dispose an object will perform any cleanup that is needed and will clear. </param>
        /// <returns> True if removed successfully. </returns>
        public bool RemoveObject(GameObject gameObject, bool dispose)
        {
            if (gameObject == null)
            {
                throw new ArgumentNullException("gameObject");
            }
            if (string.IsNullOrEmpty(gameObject.Name) || gameObject.ParentContainer != this)
            {
                return false;
            }
            GameObject current;
            if (_objects.TryGetValue(gameObject.Name, out current) && gameObject == current && _objects.Remove(gameObject.Name))
            {
                gameObject.NameChanging -= HandleObjectNameChanging;
                gameObject.RemoveFromParentContainer();
                OnRemoveObject(gameObject);
                if (dispose)
                {
                    gameObject.Dispose();
                }
                return true;
            }
            return false;
        }

        /// <summary>
        /// 	Remove all game objects from the container.
        /// </summary>
        /// <param name="dispose"> Dispose an object will perform any cleanup that is needed and will clear. </param>
        public void ClearObjects(bool dispose)
        {
            var objects = _objects.Values.ToArray();
            foreach (var gameObject in objects)
            {
                gameObject.NameChanging -= HandleObjectNameChanging;
                gameObject.RemoveFromParentContainer();
                OnRemoveObject(gameObject);
                if (dispose)
                {
                    gameObject.Dispose();
                }
            }
            _objects.Clear();
        }

        #region Event Handlers

        private void HandleObjectNameChanging(object sender, PropertyChangeEventArgs<string> args)
        {
            if (!(sender is GameObject))
            {
                throw new ArgumentException(Resx.SenderIsNotAGameObject, "sender");
            }
            if (_objects.ContainsKey(args.NewValue))
            {
                throw new InvalidOperationException(Resx.DuplicatedObjectNameInContainer);
            }
            if (!string.IsNullOrEmpty(args.OldValue))
            {
                _objects.Remove(args.OldValue);
            }
            _objects[args.NewValue] = (GameObject) sender;
        }

        #endregion

        #endregion

        #region Find

        #region Find Single Object

        /// <summary>
        /// 	Searches for a GameObject in the container.
        /// </summary>
        /// <remarks>
        /// 	This method can find item which are not GameObject. To find GameObject only use the templated version.
        /// </remarks>
        /// <param name="name"> Name of the object to look up. </param>
        /// <param name="recursive"> If true, search recurses into child containers. </param>
        /// <returns> A GameObject in the container. </returns>
        public GameObject FindObject(string name, bool recursive)
        {
            if (string.IsNullOrEmpty(name))
            {
                throw new ArgumentNullException("name");
            }
            GameObject foundObject;
            if (_objects.TryGetValue(name, out foundObject))
            {
                return foundObject;
            }
            return recursive ? _containers.Values.Select(container => container.FindObject(name, true)).FirstOrDefault(gameObject => gameObject != null) : null;
        }

        /// <summary>
        /// 	Searches for an object of type T.
        /// </summary>
        /// <typeparam name="T"> The type of the GameObject. </typeparam>
        /// <param name="recursive"> If true, search recurses into child containers. </param>
        /// <returns> A object of the specified type in the container. </returns>
        public T FindObject<T>(bool recursive) where T : GameObject
        {
            foreach (var gameObject in _objects.Values.OfType<T>())
            {
                return gameObject;
            }
            return recursive ? _containers.Values.Select(container => container.FindObject<T>(true)).FirstOrDefault(gameObject => gameObject != null) : null;
        }

        /// <summary>
        /// 	Searches for an object of type T.
        /// </summary>
        /// <typeparam name="T"> The type of the GameObject. </typeparam>
        /// <param name="name"> Name of the object to look up. </param>
        /// <param name="recursive"> If true, search recurses into child containers. </param>
        /// <returns> A object of the specified type in the container. </returns>
        public T FindObject<T>(string name, bool recursive) where T : GameObject
        {
            if (string.IsNullOrEmpty(name))
            {
                throw new ArgumentNullException("name");
            }
            GameObject foundObject;
            if (_objects.TryGetValue(name, out foundObject))
            {
                return foundObject as T;
            }
            return recursive ? _containers.Values.Select(container => container.FindObject<T>(name, true)).FirstOrDefault(gameObject => gameObject != null) : null;
        }

        #endregion

        #region Find Objects

        /// <summary>
        /// 	Searches for and populates the results list with objects of the specified type.
        /// </summary>
        /// <typeparam name="T"> The type of the GameObject. </typeparam>
        /// <param name="recursive"> If true, search recurses into child containers. </param>
        /// <returns> A list of objects of the specified type. </returns>
        public IEnumerable<T> FindObjects<T>(bool recursive) where T : GameObject
        {
            foreach (var gameObject in _objects.Values.OfType<T>())
            {
                yield return gameObject;
            }
            if (recursive)
            {
                foreach (var gameObject in _containers.Values.Select(container => container.FindObjects<T>(true)).SelectMany(gameObjects => gameObjects))
                {
                    yield return gameObject;
                }
            }
        }

        /// <summary>
        /// 	Searches for and populates the results list with objects of the specified type.
        /// </summary>
        /// <typeparam name="T"> The type of the GameObject. </typeparam>
        /// <param name="predicate"> Restricts the range of returned objects by specified clause. </param>
        /// <param name="recursive"> If true, search recurses into child containers. </param>
        /// <returns> A list of objects of the specified type. </returns>
        public IEnumerable<T> FindObjects<T>(Func<T, bool> predicate, bool recursive) where T : GameObject
        {
            foreach (var gameObject in _objects.Values.OfType<T>().Where(predicate))
            {
                yield return gameObject;
            }
            if (recursive)
            {
                foreach (var gameObject in _containers.Values.Select(container => container.FindObjects(predicate, true)).SelectMany(gameObjects => gameObjects))
                {
                    yield return gameObject;
                }
            }
        }

        /// <summary>
        /// 	Searches for and populates the results list with objects of the specified type.
        /// </summary>
        /// <typeparam name="T"> The type of the GameObject. </typeparam>
        /// <param name="recursive"> If true, search recurses into child containers. </param>
        /// <param name="results"> A list of objects of the specified type. </param>
        public void FindObjects<T>(ref List<T> results, bool recursive) where T : GameObject
        {
            if (results == null)
            {
                throw new ArgumentNullException("results");
            }
            results.AddRange(FindObjects<T>(recursive));
        }

        /// <summary>
        /// 	Searches for and populates the results list with objects of the specified type.
        /// </summary>
        /// <typeparam name="T"> The type of the GameObject. </typeparam>
        /// <param name="predicate"> Restricts the range of returned objects by specified clause. </param>
        /// <param name="recursive"> If true, search recurses into child containers. </param>
        /// <param name="results"> A list of objects of the specified type. </param>
        public void FindObjects<T>(ref List<T> results, Func<T, bool> predicate, bool recursive) where T : GameObject
        {
            if (results == null)
            {
                throw new ArgumentNullException("results");
            }
            results.AddRange(FindObjects(predicate, recursive));
        }

        #endregion

        #region Find Objects By Group

        /// <summary>
        /// 	Searches for and populates the results list with objects of the specified type by specified Group.
        /// </summary>
        /// <typeparam name="T"> The type of the GameObject. </typeparam>
        /// <param name="group"> The specified group. </param>
        /// <param name="recursive"> If true, search recurses into child containers. </param>
        /// <returns> A list of objects of the specified type. </returns>
        public IEnumerable<T> FindObjectsByGroup<T>(Group group, bool recursive) where T : GameObject
        {
            foreach (var gameObject in _objects.Values.OfType<T>().Where(gameObject => gameObject.OfGroup(group)))
            {
                yield return gameObject;
            }
            if (recursive)
            {
                foreach (var gameObject in _containers.Values.Select(container => container.FindObjectsByGroup<T>(group, true)).SelectMany(gameObjects => gameObjects))
                {
                    yield return gameObject;
                }
            }
        }

        /// <summary>
        /// 	Searches for and populates the results list with objects of the specified type by specified Group.
        /// </summary>
        /// <typeparam name="T"> The type of the GameObject. </typeparam>
        /// <param name="group"> The specified group. </param>
        /// <param name="predicate"> Restricts the range of returned objects by specified clause. </param>
        /// <param name="recursive"> If true, search recurses into child containers. </param>
        /// <returns> A list of objects of the specified type. </returns>
        public IEnumerable<T> FindObjectsByGroup<T>(Group group, Func<T, bool> predicate, bool recursive) where T : GameObject
        {
            foreach (var gameObject in _objects.Values.OfType<T>().Where(gameObject => gameObject.OfGroup(group)).Where(predicate))
            {
                yield return gameObject;
            }
            if (recursive)
            {
                foreach (var gameObject in _containers.Values.Select(container => container.FindObjectsByGroup(group, predicate, true)).SelectMany(gameObjects => gameObjects))
                {
                    yield return gameObject;
                }
            }
        }

        /// <summary>
        /// 	Searches for and populates the results list with objects of the specified type by specified Group.
        /// </summary>
        /// <typeparam name="T"> The type of the GameObject. </typeparam>
        /// <param name="group"> The specified group. </param>
        /// <param name="recursive"> If true, search recurses into child containers. </param>
        /// <param name="results"> A list of objects of the specified type. </param>
        public void FindObjectsByGroup<T>(ref List<T> results, Group group, bool recursive) where T : GameObject
        {
            if (results == null)
            {
                throw new ArgumentNullException("results");
            }
            results.AddRange(FindObjectsByGroup<T>(group, recursive));
        }

        /// <summary>
        /// 	Searches for and populates the results list with objects of the specified type by specified Group.
        /// </summary>
        /// <typeparam name="T"> The type of the GameObject. </typeparam>
        /// <param name="group"> The specified group. </param>
        /// <param name="predicate"> Restricts the range of returned objects by specified clause. </param>
        /// <param name="recursive"> If true, search recurses into child containers. </param>
        /// <param name="results"> A list of objects of the specified type. </param>
        public void FindObjectsByGroup<T>(ref List<T> results, Group group, Func<T, bool> predicate, bool recursive) where T : GameObject
        {
            if (results == null)
            {
                throw new ArgumentNullException("results");
            }
            results.AddRange(FindObjectsByGroup(group, predicate, recursive));
        }

        #endregion

        #endregion

        #endregion

        #region Containers Operations

        #region Add & Remove & Clear

        /// <summary>
        /// 	Add a sub-container to current container. Every container which is added belongs in exactly one container.
        /// </summary>
        /// <param name="container"> New sub-container will be added. </param>
        protected virtual void OnAddContainer(Container container)
        {
        }

        /// <summary>
        /// 	Remove a sub-container from current container.
        /// </summary>
        /// <param name="container"> Sub-container will be removed. </param>
        protected virtual void OnRemoveContainer(Container container)
        {
        }

        /// <summary>
        /// 	Add a sub-container to current container. Every container which is added belongs in exactly one container.
        /// </summary>
        /// <param name="container"> New sub-container will be added. </param>
        /// <returns> True if added successfully. </returns>
        public bool AddContainer(Container container)
        {
            if (container == null)
            {
                throw new ArgumentNullException("container");
            }
            if (container == this || string.IsNullOrEmpty(container.Name) || container.IsDisposed || container.ParentContainer != null)
            {
                return false;
            }
            if (!_containers.ContainsKey(container.Name))
            {
                if (Manager != null && (container.Manager == null || container.Manager != Manager))
                {
                    return false;
                }
                container.NameChanging += HandleContainerNameChanging;
                container.AddToParentContainer(this);
                _containers.Add(container.Name, container);
                OnAddContainer(container);
                return true;
            }
            return false;
        }

        /// <summary>
        /// 	Remove a sub-container from current container.
        /// </summary>
        /// <param name="container"> Sub-container will be removed. </param>
        /// <returns> True if removed successfully. </returns>
        public bool RemoveContainer(Container container)
        {
            return RemoveContainer(container, false);
        }

        /// <summary>
        /// 	Remove a sub-container from current container.
        /// </summary>
        /// <param name="container"> Sub-container will be removed. </param>
        /// <param name="dispose"> Dispose an object will perform any cleanup that is needed and will clear. </param>
        /// <returns> True if removed successfully. </returns>
        public bool RemoveContainer(Container container, bool dispose)
        {
            if (container == null)
            {
                throw new ArgumentNullException("container");
            }
            if (container == this || string.IsNullOrEmpty(container.Name) || container.ParentContainer != this)
            {
                return false;
            }
            Container current;
            if (_containers.TryGetValue(container.Name, out current) && container == current && _containers.Remove(container.Name))
            {
                container.NameChanging -= HandleContainerNameChanging;
                container.RemoveFromParentContainer();
                OnRemoveContainer(container);
                if (dispose)
                {
                    container.Dispose();
                }
                return true;
            }
            return false;
        }

        /// <summary>
        /// 	Remove all containers from the container.
        /// </summary>
        /// <param name="dispose"> Dispose an object will perform any cleanup that is needed and will clear. </param>
        public void ClearContainers(bool dispose)
        {
            var containers = _containers.Values.ToArray();
            foreach (var container in containers)
            {
                container.NameChanging -= HandleContainerNameChanging;
                container.RemoveFromParentContainer();
                OnRemoveContainer(container);
                if (dispose)
                {
                    container.Dispose();
                }
            }
            _containers.Clear();
        }

        #region Event Handlers

        private void HandleContainerNameChanging(object sender, PropertyChangeEventArgs<string> args)
        {
            if (!(sender is Container))
            {
                throw new ArgumentException(Resx.SenderIsNotAContainer, "sender");
            }
            if (_containers.ContainsKey(args.NewValue))
            {
                throw new InvalidOperationException(Resx.DuplicatedContainerNameInContainer);
            }
            if (!string.IsNullOrEmpty(args.OldValue))
            {
                _containers.Remove(args.OldValue);
            }
            _containers[args.NewValue] = (Container) sender;
        }

        #endregion

        #endregion

        #region Find

        #region Find Single Container

        /// <summary>
        /// 	Searches for a ParentContainer in the container.
        /// </summary>
        /// <remarks>
        /// 	This method can find item which are not ParentContainer. To find ParentContainer only use the templated version.
        /// </remarks>
        /// <param name="name"> Name of the container to look up. </param>
        /// <param name="recursive"> If true, search recurses into child containers. </param>
        /// <returns> A ParentContainer in the container. </returns>
        public Container FindContainer(string name, bool recursive)
        {
            if (string.IsNullOrEmpty(name))
            {
                throw new ArgumentNullException("name");
            }
            Container foundContainer;
            if (_containers.TryGetValue(name, out foundContainer))
            {
                return foundContainer;
            }
            return recursive ? _containers.Values.Select(container => container.FindContainer(name, true)).FirstOrDefault(container => container != null) : null;
        }

        /// <summary>
        /// 	Searches for an container of type T.
        /// </summary>
        /// <typeparam name="T"> The type of the ParentContainer. </typeparam>
        /// <param name="recursive"> If true, search recurses into child containers. </param>
        /// <returns> A container of the specified type in the container. </returns>
        public T FindContainer<T>(bool recursive) where T : Container
        {
            foreach (var container in _containers.Values.OfType<T>())
            {
                return container;
            }
            return recursive ? _containers.Values.Select(container => container.FindContainer<T>(true)).FirstOrDefault(container => container != null) : null;
        }

        /// <summary>
        /// 	Searches for an container of type T.
        /// </summary>
        /// <typeparam name="T"> The type of the ParentContainer. </typeparam>
        /// <param name="name"> Name of the container to look up. </param>
        /// <param name="recursive"> If true, search recurses into child containers. </param>
        /// <returns> A container of the specified type in the container. </returns>
        public T FindContainer<T>(string name, bool recursive) where T : Container
        {
            if (string.IsNullOrEmpty(name))
            {
                throw new ArgumentNullException("name");
            }
            Container foundContainer;
            if (_containers.TryGetValue(name, out foundContainer))
            {
                return foundContainer as T;
            }
            return recursive ? _containers.Values.Select(container => container.FindContainer<T>(name, true)).FirstOrDefault(container => container != null) : null;
        }

        #endregion

        #region Find Containers

        /// <summary>
        /// 	Searches for and populates the results list with containers of the specified type.
        /// </summary>
        /// <typeparam name="T"> The type of the ParentContainer. </typeparam>
        /// <param name="recursive"> If true, search recurses into child containers. </param>
        /// <returns> A list of containers of the specified type. </returns>
        public IEnumerable<T> FindContainers<T>(bool recursive) where T : Container
        {
            foreach (var container in _containers.Values.OfType<T>())
            {
                yield return container;
            }
            if (recursive)
            {
                foreach (var container in _containers.Values.Select(container => container.FindContainers<T>(true)).SelectMany(containers => containers))
                {
                    yield return container;
                }
            }
        }

        /// <summary>
        /// 	Searches for and populates the results list with containers of the specified type.
        /// </summary>
        /// <typeparam name="T"> The type of the ParentContainer. </typeparam>
        /// <param name="predicate"> Restricts the range of returned containers by specified clause. </param>
        /// <param name="recursive"> If true, search recurses into child containers. </param>
        /// <returns> A list of containers of the specified type. </returns>
        public IEnumerable<T> FindContainers<T>(Func<T, bool> predicate, bool recursive) where T : Container
        {
            foreach (var container in _containers.Values.OfType<T>().Where(predicate))
            {
                yield return container;
            }
            if (recursive)
            {
                foreach (var container in _containers.Values.Select(container => container.FindContainers(predicate, true)).SelectMany(containers => containers))
                {
                    yield return container;
                }
            }
        }

        /// <summary>
        /// 	Searches for and populates the results list with containers of the specified type.
        /// </summary>
        /// <typeparam name="T"> The type of the ParentContainer. </typeparam>
        /// <param name="recursive"> If true, search recurses into child containers. </param>
        /// <param name="results"> A list of containers of the specified type. </param>
        public void FindContainers<T>(ref List<T> results, bool recursive) where T : Container
        {
            if (results == null)
            {
                throw new ArgumentNullException("results");
            }
            results.AddRange(FindContainers<T>(recursive));
        }

        /// <summary>
        /// 	Searches for and populates the results list with containers of the specified type.
        /// </summary>
        /// <typeparam name="T"> The type of the ParentContainer. </typeparam>
        /// <param name="predicate"> Restricts the range of returned containers by specified clause. </param>
        /// <param name="recursive"> If true, search recurses into child containers. </param>
        /// <param name="results"> A list of containers of the specified type. </param>
        public void FindContainers<T>(ref List<T> results, Func<T, bool> predicate, bool recursive) where T : Container
        {
            if (results == null)
            {
                throw new ArgumentNullException("results");
            }
            results.AddRange(FindContainers(predicate, recursive));
        }

        #endregion

        #endregion

        #endregion

        #region Parent Container Operations

        #region Fields & Properties

        private Container _parentContainer;

        /// <summary>
        /// 	Parent container which this object is in.
        /// </summary>
        /// <remarks>
        /// 	Objects can be moved between containers by setting this property.
        /// </remarks>
        [CloneIgnore]
        public Container ParentContainer
        {
            get
            {
                return _parentContainer;
            }
        }

        #endregion

        #region Add & Remove

        /// <summary>
        /// 	Called after the object is attached to a parent container.
        /// </summary>
        /// <remarks>
        /// 	Override this to hook up functionality to the parent container.
        /// </remarks>
        protected virtual void OnAddToParentContainer()
        {
        }

        /// <summary>
        /// 	Called when the object is being removed from its parent container, but before it has actually occurred.
        /// </summary>
        /// <remarks>
        /// 	Override this to unhook functionality from the parent container.
        /// </remarks>
        protected virtual void OnRemoveFromParentContainer()
        {
        }

        /// <summary>
        /// 	Adds to the specified parent container.
        /// </summary>
        /// <param name="container"> The parent container to add to. </param>
        internal void AddToParentContainer(Container container)
        {
            if (container != null && container != this && _parentContainer == null)
            {
                try
                {
                    _parentContainer = container;
                    OnAddToParentContainer();
                }
                catch (Exception ex)
                {
                    //TODO: Trace
                    Debug.WriteLine(ex.Message);
                }
            }
        }

        /// <summary>
        /// 	Removes From this instance from its parent container.
        /// </summary>
        internal void RemoveFromParentContainer()
        {
            if (_parentContainer != null)
            {
                try
                {
                    OnRemoveFromParentContainer();
                    _parentContainer = null;
                }
                catch (Exception ex)
                {
                    //TODO: Trace
                    Debug.WriteLine(ex.Message);
                }
            }
        }

        #endregion

        #endregion
    }
}